package com.vhall.component.service.room.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.vhall.component.dao.account.AccountMapper;
import com.vhall.component.dao.room.*;
import com.vhall.component.dao.vote.RoomVoteLkMapper;
import com.vhall.component.entity.account.AccountEntity;
import com.vhall.component.entity.account.enums.GuoXinStaffEnums;
import com.vhall.component.entity.chat.vo.ChannelSwitchOptionReqVo;
import com.vhall.component.entity.rebroadcast.RebroadCastEntity;
import com.vhall.component.entity.room.RoomConfig;
import com.vhall.component.entity.room.dto.RoomWatermarkDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.*;
import com.vhall.component.entity.room.enums.*;
import com.vhall.component.entity.room.vo.*;
import com.vhall.component.entity.subject.enums.StatusEnum;
import com.vhall.component.entity.vote.RoomVoteLkEntity;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.utils.CollectionUtils;
import com.vhall.component.plugin.common.annotation.RedisSpinLock;
import com.vhall.component.plugin.common.constant.PaasCommonConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.enums.DeleteStatusEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.support.VssTokenThreadLocal;
import com.vhall.component.plugin.common.utils.BeanCopyUtil;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.core.service.GuoXinService;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.account.LiveWatchCheckHandle;
import com.vhall.component.service.goods.common.GoodsConstant;
import com.vhall.component.service.room.RoomCacheService;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.room.RoomService;
import com.vhall.component.service.room.concant.RoomConstant;
import com.vhall.component.service.room.manager.RoomMqManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.connection.stream.StringRecord;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.vhall.component.entity.room.enums.LimitTypeEnum.PHONE;
import static com.vhall.component.entity.room.enums.LimitTypeEnum.WATCH_ALL;
import static com.vhall.component.plugin.common.constant.RedisKey.FRONT_VERSION;
import static com.vhall.component.plugin.common.constant.RedisKey.ROOM_DEFAULT_RECORD;
import static com.vhall.component.service.pendant.common.PendantConstant.ACTIVITY_PENDANT_EXPIRE_KEY;

/**
 * @author 65782
 * @date 2021-06-08 14:40
 */
@Slf4j
@Service
public class RoomServiceImpl implements RoomService {

    @Value("${h5.redirect.url}")
    private String h5Url;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RebroadCastMapper rebroadCastMapper;

    @Autowired
    private RoomMapper roomMapper;

    @Autowired
    private RoomJoinsMapper roomJoinsMapper;

    @Autowired
    private RoomExtendsMapper roomExtendsMapper;

    @Autowired
    private SupplyMapper supplyMapper;


    @Autowired
    private PaasService paasService;

    @Value("${vhall.lottery.stream-name:lottery-stream}")
    public String streamName;

    @Autowired
    private RoomCacheService roomCacheService;

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private RoomMqManager roomMqManager;

    @Autowired
    private RoomConfigMapper roomConfigMapper;

    @Autowired
    private PushStreamMapper pushStreamMapper;
    @Resource
    private LiveWatchCheckHandle liveWatchCheckHandle;
    public static final String ROOM_ID = "room_id";
    /**
     * 是否开启房间审核
     * 1为开启
     * 0为关闭房间审核
     */
    @Value("${project.room.audit:0}")
    private Integer roomAuditWitch;

    @Resource
    private RoomReservationMapper reservationMapper;

    @Resource
    private LiveReportMapper liveReportMapper;

    @Resource
    private LiveReportConfigMapper liveReportConfigMapper;

    @Resource
    private RoomReservationMapper roomReservationMapper;
    @Resource
    private RoomVoteLkMapper roomVoteLkMapper;

    @Value("${vhall.have_sdk_func_version}")
    private String haveSdkFuncVersion;

    @Resource
    private RiskWarningMapper riskWarningMapper;
    @Resource
    private GuoXinService guoXinService;
    @Resource
    private RoomReservationThirdMapper roomReservationThirdMapper;
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private RoomGoodsMapper roomGoodsMapper;

    @Override
    public RoomAttrRespVO getAttributes(RoomAttrVO reqVO) {
        String roomId = reqVO.getRoomId();
        RoomAttrRespVO vo = new RoomAttrRespVO();
        HashMap<String, Object> streamMap = new HashMap<>(4);
        String rebroadcastRoomId = "";
        String rebroadcastChannelId = "";
        RebroadCastEntity rebroadCastEntity = getRebroad(roomId);
        if (rebroadCastEntity != null && StringUtils.isNotEmpty(rebroadCastEntity.getSourceRoomId())) {
            rebroadcastRoomId = rebroadCastEntity.getSourceRoomId();
            RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(rebroadCastEntity.getSourceRoomId());
            if (roomsEntityDTO != null) {
                rebroadcastChannelId = roomsEntityDTO.getChannelId();

                // 如果被转播的直播间不是直播中状态
                if (roomsEntityDTO.getStatus() != 1) {
                    rebroadcastRoomId = "";
                    rebroadcastChannelId = "";
                }
            }
        }
        Object docVal = redisTemplate.opsForHash().get(RedisKey.ROOM_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.IS_OPEN_DOCUMENT);
        if (docVal != null) {
            vo.setIsDoc(Integer.parseInt(docVal.toString()));
        }
        Object isHandsup = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.IS_HANDSUP);
        if (isHandsup != null) {
            vo.setIsHandsup(Integer.parseInt(isHandsup.toString()));
        }
        Object layOut = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.LAYOUT);
        Object definition = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.DEFINITION);
        if (layOut != null) {
            streamMap.put("layout", layOut);
        }
        if (definition != null) {
            streamMap.put("definition", definition);
        }
        //获取各功能开关状态信息

        List<RoomConfig> configList = roomConfigMapper.selectList(new LambdaQueryWrapper<RoomConfig>()
                .eq(RoomConfig::getRoomId, roomId)
        );

        Map<String, Integer> collect = new HashMap<>();
        if (CollUtil.isNotEmpty(configList)) {
            for (RoomConfig roomConfig : configList) {
                collect.put(roomConfig.getConfigKey(), roomConfig.getConfigValue());
            }
        }
        vo.setSwitchState(collect);
        vo.setStream(streamMap);
        vo.setSpeakerList(getSpeakers(roomId));
        vo.setRebroadcast(rebroadcastRoomId);
        vo.setRebroadcastChannelId(rebroadcastChannelId);
        return getRoomAttrRespVO(vo, roomId);
    }


    /**
     * 获取上麦用户
     *
     * @param roomId PAAS直播房间id
     * @return 上麦用户集合
     */
    private List<SpeakUsersVO> getSpeakers(String roomId) {
        List<String> speakerList = redisTemplate.opsForHash().values(RedisKey.INTERACT_SPEAKER + roomId);
        List<SpeakUsersVO> speakers = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(speakerList)) {
            for (String str : speakerList) {
                String speakStr = JSON.parse(str).toString();
                SpeakUsersVO speak = JSON.parseObject(speakStr, SpeakUsersVO.class);
                speakers.add(speak);
            }
        }
        return speakers;
    }

    /**
     * 从缓存获取字段数据及处理字段赋值
     *
     * @param vo     请求入参
     * @param roomId PAAS直播房间id
     * @return RoomAttrRespVO
     */
    private RoomAttrRespVO getRoomAttrRespVO(RoomAttrRespVO vo, String roomId) {
        RoomJoinsEntity roomJoinsEntity = getHostRoomJoin(roomId);
        Object mainScreen = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.MAIN_SCREEN);
        if (mainScreen != null) {
            vo.setMainScreen(String.valueOf(mainScreen));
        } else {
            if (roomJoinsEntity != null) {
                vo.setMainScreen(String.valueOf(roomJoinsEntity.getAccountId()));
            }
        }
        Object docPermission = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.DOC_PERMISSION);
        if (docPermission != null) {
            vo.setDocPermission(String.valueOf(docPermission));
        } else {
            if (roomJoinsEntity != null) {
                vo.setDocPermission(String.valueOf(roomJoinsEntity.getAccountId()));
            }
        }
        Object startType = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.START_TYPE);
        if (startType != null) {
            vo.setStartType(String.valueOf(startType));
        }
        Object allBanned = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.ALL_BANNED);
        if (allBanned != null) {
            vo.setAllBanned(Integer.parseInt(allBanned.toString()));
        }
        Object isDesktop = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.IS_DESKTOP);
        if (isDesktop != null) {
            vo.setIsDesktop(Integer.parseInt(isDesktop.toString()));
        }
        Map<String, Object> tool = redisTemplate.opsForHash().entries(RedisKey.INTERACT_TOOL + roomId);
        log.info("comp:inav:tool:{}", tool);
        if (!tool.containsKey(RedisKey.INTERACT_TOOL_FILED)) {
            Long vote = getLastVote(roomId);
            if (vote != null) {
                tool.put(RedisKey.INTERACT_TOOL_FILED, String.valueOf(vote));
            }
        }
        if (!tool.containsKey(GoodsConstant.LIVE_GOODS_ENTRANCE_SWITCH_KEY)) {
            String key = roomId + ":goods";
            if (redisTemplate.hasKey(key)) {
                Object o = redisTemplate.opsForValue().get(key);
                Integer count = Integer.parseInt(String.valueOf(o));
                tool.put(GoodsConstant.LIVE_GOODS_ENTRANCE_SWITCH_KEY, count > 0 ? 1 : 0);
            } else {
                RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
                LambdaQueryWrapper<RoomGoodsEntity> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.eq(RoomGoodsEntity::getIlId, roomInfo.getIlId());
                queryWrapper.eq(RoomGoodsEntity::getIsDeleted, 0);
                Integer count = roomGoodsMapper.selectCount(queryWrapper);

                tool.put(GoodsConstant.LIVE_GOODS_ENTRANCE_SWITCH_KEY, count > 0 ? 1 : 0);
                redisTemplate.opsForValue().set(key, count, 30, TimeUnit.MINUTES);
            }
        }
        vo.setTool(tool);
        Map<String, Object> entries = redisTemplate.opsForHash().entries(RedisKey.INTERACT_TOOL_RECORDS + roomId);
        vo.setToolRecords(entries);
        Object isInviteCard = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.OPEN_INVITE_CARD);
        if (isInviteCard != null) {
            vo.setIsInviteCard(Integer.parseInt(isInviteCard.toString()));
        }

        Object value = redisTemplate.opsForValue().get(ACTIVITY_PENDANT_EXPIRE_KEY + roomId);
        JSONObject pendant = new JSONObject();
        if (value != null) {
            pendant = JSON.parseObject(value.toString());
        }
        vo.setPendant(pendant);

        return vo;
    }

    private Long getLastVote(String roomId) {
        LambdaQueryWrapper<RoomVoteLkEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RoomVoteLkEntity::getRoomId, roomId);
        queryWrapper.eq(RoomVoteLkEntity::getPublish, 1);
        queryWrapper.orderByDesc(RoomVoteLkEntity::getUpdatedAt);
        queryWrapper.last(" limit 1");
        RoomVoteLkEntity roomVoteLkEntity = roomVoteLkMapper.selectOne(queryWrapper);
        if (roomVoteLkEntity != null) {
            return roomVoteLkEntity.getVoteId();
        }
        return null;
    }

    /**
     * 查询活动转播拉流记录
     *
     * @param roomId 房间id
     * @return php得 规则 是  opModel+RebroadCastModel+StartInfoByRoomId:roomId
     */
    public RebroadCastEntity getRebroad(String roomId) {
        String cacheKey = RedisKey.REBROADCAST_INFO + roomId;
        //从缓存获取活动转播拉流记录信息
        RebroadCastEntity rebroadCastEntity = roomCacheService.getRebroadCache(roomId);
        if (Objects.nonNull(rebroadCastEntity)) {
            return rebroadCastEntity;
        }
        return cacheAndHandleGetRebroad(roomId, cacheKey);
    }

    @Override
    @RedisSpinLock(redisKey = "#cacheKey", expireTime = 10 * 1000)
    public RebroadCastEntity cacheAndHandleGetRebroad(String roomId, String cacheKey) {
        RebroadCastEntity rebroadCastEntity = roomCacheService.getRebroadCache(roomId);
        if (Objects.isNull(rebroadCastEntity)) {
            LambdaQueryWrapper<RebroadCastEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(RebroadCastEntity::getRoomId, roomId);
            wrapper.eq(RebroadCastEntity::getStatus, 1);
            rebroadCastEntity = rebroadCastMapper.selectOne(wrapper);
            if (Objects.isNull(rebroadCastEntity)) {
                return null;
            }
            //缓存活动转播拉流记录
            roomCacheService.cacheRebroad(rebroadCastEntity);
        }
        return rebroadCastEntity;
    }


    /**
     * 查询房间信息
     *
     * @param roomId InfoByRoomId
     * @return 返回值
     */
    @Override
    public RoomsEntity getRoomByRoomId(String roomId) {
        RoomsEntity entity;
        Object cast = redisTemplate.opsForValue().get(RedisKey.ROOM_INFO_BY_ROOM_ID + roomId);
        if (cast != null) {
            RoomsEntityDTO roomsEntityDTO = JSON.parseObject(cast.toString(), RoomsEntityDTO.class);
            entity = new RoomsEntity();
            BeanCopyUtil.copy(roomsEntityDTO, entity);
        } else {
            LambdaQueryWrapper<RoomsEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(RoomsEntity::getRoomId, roomId);
            entity = roomMapper.selectOne(wrapper);
        }
        return entity;
    }

    @Override
    public Boolean masterStatusNotice(MasterStatusNoticeVO reqVO) {
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (!roomsEntityDTO.getIlId().equals(reqVO.getIlId())) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);

        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT);
        // 构建消息请求体
        JSONObject object = new JSONObject();
        object.put("type", reqVO.getStatus() == 1 ? "master_online" : "master_offline");
        object.put("push_time", df.format(LocalDateTime.now()));
        object.put("msg", reqVO.getStatus() == 1 ? "主持人已上线" : "主持人暂时离开");
        String body = JsonUtil.toJsonString(object);
        JSONObject objects = null;
        try {
            objects = paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
        } catch (Exception e) {
            log.error("masterStatusNotice error:", e);
        }
        return objects != null;
    }

    /**
     * 查询房间加入信息
     *
     * @param roomId 房间id
     * @return 返回值
     */
    public RoomJoinsEntity getHostRoomJoin(String roomId) {
        RoomJoinsEntity entity = roomCacheService.getCachedHostRoomJoin(roomId);
        if (Objects.isNull(entity)) {
            LambdaQueryWrapper<RoomJoinsEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(RoomJoinsEntity::getRoomId, roomId);
            wrapper.eq(RoomJoinsEntity::getRoleName, com.vhall.component.plugin.common.constant.RoomConstant.HOST)
                    .last(RoomConstant.LIMIT_1);
            entity = roomJoinsMapper.selectOne(wrapper);
            // 将主持人参会信息单独缓存
            roomCacheService.cacheHostRoomJoin(entity);
        }
        return entity;
    }

    @Override
    public RoomExtendRespVO getRoomExtendsInfo(RoomAttrVO reqVO) {
        RoomExtendRespVO vo = new RoomExtendRespVO();
        String roomId = reqVO.getRoomId();
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
//        开启房间审核并且房间审核状态不为审核通过开启直播时抛出异常
        checkRoomLiveStartAudit(roomsEntityDTO);
        RoomExtendsEntity roomExtendsEntity = getRoomExtends(roomId);
        return buildResponseVO(roomsEntityDTO, roomExtendsEntity, vo);
    }

    /**
     * 查询房间额外信息
     *
     * @param roomId 房间id
     * @return 返回值
     */
    public RoomExtendsEntity getRoomExtends(String roomId) {
        String cacheKey = RedisKey.ROOM_EXTEND_CACHE + roomId;
        //从缓存获取 房间扩展信息
        RoomExtendsEntity roomExtendsEntity = roomCacheService.getRoomExtendCache(roomId);
        if (Objects.nonNull(roomExtendsEntity)) {
            return roomExtendsEntity;
        }
        return cacheAndHandleGetRoomExtend(roomId, cacheKey);
    }

    @Override
    @RedisSpinLock(redisKey = "#cacheKey", expireTime = 10 * 1000)
    public RoomExtendsEntity cacheAndHandleGetRoomExtend(String roomId, String cacheKey) {
        //从缓存获取 房间扩展信息
        RoomExtendsEntity roomExtendsEntity = roomCacheService.getRoomExtendCache(roomId);
        if (Objects.isNull(roomExtendsEntity)) {
            LambdaQueryWrapper<RoomExtendsEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(RoomExtendsEntity::getRoomId, roomId);
            wrapper.eq(RoomExtendsEntity::getEndType, 0);
            wrapper.orderByDesc(RoomExtendsEntity::getCreatedAt);
            wrapper.last(" limit 1 ");
            roomExtendsEntity = roomExtendsMapper.selectOne(wrapper);
            if (Objects.isNull(roomExtendsEntity)) {
                return null;
            }
            //放入缓存
            roomCacheService.cacheRoomExtend(roomExtendsEntity);
        }
        return roomExtendsEntity;
    }

    @Override
    public JSONObject submitVODToLive(SubmitVODToLiveVO reqVO) {
        Map<String, Object> param = new HashMap<>(8);
        param.put("vod_id", reqVO.getVodId());
        param.put("cmd", reqVO.getCmd());

        param.put(ROOM_ID, reqVO.getRoomId());

        param.put("loop", reqVO.getLoop());
        if (StringUtils.isNotBlank(reqVO.getChannelId())) {
            param.put("channel_id", reqVO.getChannelId());
        }

        return paasService.submitVODToLive(param);
    }

    @Override
    public JSONObject getStreamStatus(String roomIds) {
        return paasService.getStreamStatus(roomIds);
    }

    @Override
    public RoomWatermarkDTO getRoomWatermark(Integer watermarkId) {
        RoomWatermarkDTO vo = new RoomWatermarkDTO();
        if (watermarkId == null) {
            String tempStr = roomInnerService.getDefaultWatermarkTemplateId();
            if (StringUtils.isNotEmpty(tempStr)) {
                watermarkId = Integer.valueOf(tempStr);
            }
        }
        if (watermarkId == null || watermarkId == 0) {
            return vo;
        }
        try {
            //  paas获取水印信息
            JSONObject jsonObject = paasService.getWatermarkInfo(watermarkId);
            vo.setWatermarkImage(jsonObject.getString("watermark_img"));
            vo.setWatermarkPosition(jsonObject.getInteger("watermark_positiontype"));
            vo.setWatermarkTtempId(watermarkId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return vo;
    }

    @Override
    public Boolean genDefaultRecord(EndLiveVO reqVO) {
        stringRedisTemplate.opsForValue().set(ROOM_DEFAULT_RECORD + reqVO.getRoomId(), "0");
        log.info(MessageFormat.format("当前房间：【{0}】，设置默认回放", reqVO.getRoomId()));
        return true;
    }

    @Override
    public Boolean reservationOrNot(ReservationVO reservationVO) {

        Token token = TokenThreadLocal.getToken();
        LambdaQueryWrapper<RoomReservationEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomReservationEntity::getIlId, reservationVO.getIlId());
        wrapper.eq(RoomReservationEntity::getAccountId, token.getAccountId());
        RoomReservationEntity roomReservationEntity = reservationMapper.selectOne(wrapper);

        if (roomReservationEntity == null && reservationVO.getType().equals(StatusEnum.OPEN.getValue())) {
            RoomReservationEntity entity = new RoomReservationEntity();
            entity.setAccountId(token.getAccountId());
            entity.setIlId(reservationVO.getIlId());
            reservationMapper.insert(entity);
        } else if (roomReservationEntity != null) {
            roomReservationEntity.setType(reservationVO.getType());
            reservationMapper.updateById(roomReservationEntity);
        }
        setGuoXinReservation(reservationVO.getIlId(), token.getAccountId(), reservationVO.getType());
        return true;
    }

    private void setGuoXinReservation(Integer ilId, Integer accountId, Integer flag) {
        try {
            AccountEntity accountEntity = accountMapper.selectById(accountId);
            RoomsEntity roomsEntity = roomMapper.selectById(ilId);
            LambdaQueryWrapper<RoomReservationThirdEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(RoomReservationThirdEntity::getIlId, roomsEntity.getIlId());
            queryWrapper.eq(RoomReservationThirdEntity::getPhone, accountEntity.getPhone());
            queryWrapper.last(" limit 1");
            RoomReservationThirdEntity reservationThirdEntity = roomReservationThirdMapper.selectOne(queryWrapper);
            if (reservationThirdEntity != null) {
                reservationThirdEntity.setStatus(flag);
                reservationThirdEntity.setUpdateTime(new Date());
                roomReservationThirdMapper.updateById(reservationThirdEntity);
            } else {
                RoomReservationThirdEntity entity = new RoomReservationThirdEntity();
                entity.setRoomId(roomsEntity.getRoomId());
                entity.setIlId(ilId);
                entity.setRoomTitle(roomsEntity.getSubject());
                entity.setRoomIntroduction(roomsEntity.getIntroduction());
                entity.setRoomStartTime(roomsEntity.getStartTime());
                entity.setRoomStatus(roomsEntity.getStatus());
                entity.setPhone(accountEntity.getPhone().toString());
                String redirectUlr = StrUtil.replace(h5Url, "il_id", String.valueOf(ilId));
                entity.setH5Url(redirectUlr);
                roomReservationThirdMapper.insert(entity);
            }
        }catch (Exception e){
            log.error("保存预约信息失败：{}", e);
        }

    }

    @Override
    public ReportRoomRspVO reportRoom(ReportRoomReqVO vo) {

        VssToken token = VssTokenThreadLocal.getToken();
        LiveReportEntity liveReportEntity = new LiveReportEntity();
        liveReportEntity.setReportId(vo.getReportId());
        liveReportEntity.setRoomId(vo.getRoomId());
        liveReportEntity.setAccountId(token.getThirdPartyUserId());
        liveReportEntity.setDesc(vo.getDesc());
        liveReportEntity.setDeleted(DeleteStatusEnum.NO_DELETE.getStatus());
        liveReportMapper.insert(liveReportEntity);

        ReportRoomRspVO reportRoomRspVO = new ReportRoomRspVO();
        BeanCopyUtil.copy(liveReportEntity, reportRoomRspVO);

        return reportRoomRspVO;
    }

    @Override
    public List<ReportConfigRspVO> getReportConfigList(ReportConfigReqVO vo) {
        LambdaQueryWrapper<LiveReportConfigEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(LiveReportConfigEntity::getDeleted, DeleteStatusEnum.NO_DELETE.getStatus());
        wrapper.eq(StringUtils.isNotBlank(vo.getDesc()), LiveReportConfigEntity::getDesc, vo.getDesc());
        List<LiveReportConfigEntity> liveReportConfigEntities = liveReportConfigMapper.selectList(wrapper);
        return BeanCopyUtil.batchCopy(liveReportConfigEntities, ReportConfigRspVO::new);

    }

    @Override
    public Boolean judgeReservationOrNot(CheckIsAnchorReqVO reservationVO) {
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reservationVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        Token token = TokenThreadLocal.getToken();

        LambdaQueryWrapper<RoomReservationEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomReservationEntity::getIlId, roomsEntityDTO.getIlId());
        wrapper.eq(RoomReservationEntity::getAccountId, token.getAccountId());
        wrapper.eq(RoomReservationEntity::getType, StatusEnum.OPEN.getValue());
        List<RoomReservationEntity> roomReservationEntities = roomReservationMapper.selectList(wrapper);
        return CollectionUtils.isNotEmpty(roomReservationEntities);
    }

    @Override
    public String getHaveSDKVersion() {
        Object o = redisTemplate.opsForValue().get(FRONT_VERSION);
        if (o != null) {
            return o.toString();
        }
        return haveSdkFuncVersion;
    }

    @Override
    public Boolean settingHaveSDKVersion(String version) {
        Preconditions.checkArgument(StringUtils.isNotBlank(version), "version 不能为空！");
        redisTemplate.opsForValue().set(FRONT_VERSION, version);
        return Boolean.TRUE;
    }

    @Override
    public RoomLimitTypeRspVO getLimitType(String roomId) {
        Preconditions.checkArgument(StringUtils.isNotBlank(roomId), "房间号不能为空！");

        RoomLimitTypeRspVO roomLimitTypeRspVO = new RoomLimitTypeRspVO();

        String value = "";
        String description = "";

        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        Integer limitType = roomsEntityDTO.getLimitType();
        LimitTypeEnum parse = LimitTypeEnum.parse(limitType);
        switch (Objects.requireNonNull(parse)) {
            case WATCH_ALL:
                value = "WATCH_ALL";
                description = WATCH_ALL.getDescription();
                break;

            case PHONE:
                value = "PHONE";
                description = PHONE.getDescription();
                break;

            case ORDINARY_MEMBERS:
            case SILVER_MEMBER:
            case GOLD_MEMBER:
            case PLATINUM_MEMBER:
                value = "MEMBERS";
                description = "会员等级";
                break;

            case FUND_ACCOUNT1:
            case FUND_ACCOUNT2:
            case FUND_ACCOUNT3:
            case FUND_ACCOUNT4:
            case FUND_ACCOUNT5:
                value = "FUND_ACCOUNT";
                description = "资金账号";
                break;
            default:
                break;


        }
        roomLimitTypeRspVO.setValue(value);
        roomLimitTypeRspVO.setDescription(description);

        return roomLimitTypeRspVO;
    }

    @Override
    public RiskWarningSmallVO getRoomRiskWarning(String roomId) {
        RiskWarningSmallVO vo = new RiskWarningSmallVO();
        RoomsEntity roomByRoomId = getRoomByRoomId(roomId);
        if (roomByRoomId == null) {
            return vo;
        }
        Integer liveContentType = roomByRoomId.getLiveContentType();
        if (liveContentType == null) {
            return vo;
        }
        if (redisTemplate.hasKey(RedisKey.RISK_WARNING_KEY)) {
            if (redisTemplate.opsForHash().hasKey(RedisKey.RISK_WARNING_KEY, liveContentType.toString())) {
                Object o = redisTemplate.opsForHash().get(RedisKey.RISK_WARNING_KEY, liveContentType.toString());
                if (o != null) {
                    String content = String.valueOf(o);
                    vo.setContent(content);
                    vo.setLiveContentType(liveContentType);
                } else {
                    RiskWarningEntity riskWarning = cacheRiskWarning(liveContentType);
                    vo.setLiveContentType(liveContentType);
                    vo.setContent(riskWarning.getContent());
                }
            } else {
                RiskWarningEntity riskWarning = cacheRiskWarning(liveContentType);
                vo.setLiveContentType(liveContentType);
                vo.setContent(riskWarning.getContent());
            }
        } else {
            RiskWarningEntity riskWarning = cacheRiskWarning(liveContentType);
            vo.setLiveContentType(liveContentType);
            vo.setContent(riskWarning.getContent());
        }
        return vo;
    }

    @Override
    public LiveWatchCheckResponseVO liveWatchCheck(LiveWatchCheckRequestVO vo) {

        LiveWatchCheckResponseVO result = new LiveWatchCheckResponseVO();
        Token token = TokenThreadLocal.getToken();
        if (Objects.equals(GuoXinStaffEnums.STAFF.getValue(), token.getStaff())) {
            result.setCheckResult(LiveWatchLimitCheckEnum.CHECK_PASSED.getValue());
            return result;
        }
        RoomsEntityDTO roomInfoById = Optional.ofNullable(roomInnerService.getRoomInfoById(vo.getIlId())).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));
        Integer liveType = roomInfoById.getLiveContentType();
        vo.setLiveContentType(liveType);
        result = liveWatchCheckHandle.liveWatchCheck(vo);
        return result;
    }

    @Override
    public Boolean setBeautyCoefficient(Integer number) {
        Token token = TokenThreadLocal.getToken();
        String key = "comp:beauty:coefficient:" + token.getAccountId();
        redisTemplate.opsForValue().set(key, String.valueOf(number));
        return null;
    }

    @Override
    public Integer getBeautyCoefficient() {
        Token token = TokenThreadLocal.getToken();
        String key = "comp:beauty:coefficient:" + token.getAccountId();
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            Object o = redisTemplate.opsForValue().get(key);
            try {
                return Integer.parseInt(String.valueOf(o));
            } catch (Exception e) {
                return 0;
            }
        }
        return 0;
    }

    @Override
    public Long getVirtualPV(Integer ilId) {
        return roomInnerService.getVirtualPV(ilId);
    }

    private RiskWarningEntity cacheRiskWarning(Integer liveContentTye) {
        RiskWarningEntity riskWarning = getRiskWarning(liveContentTye);
        redisTemplate.opsForHash().put(RedisKey.RISK_WARNING_KEY, liveContentTye.toString(), riskWarning.getContent());
        redisTemplate.expire(RedisKey.RISK_WARNING_KEY, 3, TimeUnit.DAYS);
        return riskWarning;
    }

    private RiskWarningEntity getRiskWarning(Integer liveContentTye) {
        LambdaQueryWrapper<RiskWarningEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RiskWarningEntity::getLiveContentType, liveContentTye);
        queryWrapper.last(" limit 1");
        return riskWarningMapper.selectOne(queryWrapper);
    }


    /**
     * 构造返回对象
     *
     * @param roomsEntityDTO 房间信息
     * @param roomExtends    房间扩展信息
     * @param vo             返回值
     * @return 返回值
     */
    private RoomExtendRespVO buildResponseVO(RoomsEntityDTO roomsEntityDTO, RoomExtendsEntity roomExtends, RoomExtendRespVO vo) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT);
        vo.setRoomId(roomsEntityDTO.getRoomId());
        vo.setRecordId(roomsEntityDTO.getRecordId());
        vo.setStatus(roomsEntityDTO.getStatus() != null ? roomsEntityDTO.getStatus() : com.vhall.component.plugin.common.constant.RoomConstant.DEFAULT_ZERO);
        if (roomsEntityDTO.getAccountId() != null) {
            vo.setAccountId(Long.parseLong(roomsEntityDTO.getAccountId().toString()));
        }
        if (roomExtends != null) {
            if (roomExtends.getStartTime() != null) {
                vo.setStartTime(df.format(roomExtends.getStartTime()));
            }
            if (roomExtends.getEndTime() != null) {
                vo.setEndTime(df.format(roomExtends.getEndTime()));
            }
        }
        LambdaQueryWrapper<RoomExtendsEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomExtendsEntity::getRoomId, roomsEntityDTO.getRoomId());
        wrapper.orderByDesc(RoomExtendsEntity::getId);
        wrapper.last(" limit 1 ");
        RoomExtendsEntity roomExtendsEntity = roomExtendsMapper.selectOne(wrapper);
        if (roomsEntityDTO.getStatus() == com.vhall.component.plugin.common.constant.RoomConstant.STATUS_STOP || roomExtendsEntity == null) {
            vo.setStartType(0);
        } else {
            vo.setStartType(roomExtendsEntity.getStartType());
        }
        return vo;
    }

    @Override
    public Boolean startLive(StartLiveVO reqVO) {
        VssToken tokenInfo = VssTokenThreadLocal.getToken();
        String roomId = reqVO.getRoomId();
        String startType = reqVO.getStartType();
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        Preconditions.checkArgument(roomsEntityDTO.getLiveType().equals(LiveRoomTypeEnum.LIVE_BROADCAST.getType()), "当前房间模式不支持手动开播！");

        Integer thirdPartyUserId = tokenInfo.getThirdPartyUserId();
        // 开启聊天审核
        openChannelSwitch(roomsEntityDTO.getChannelId(), thirdPartyUserId);
        // 校验是否是主持人
        if (checkIsAnchor(thirdPartyUserId, roomId) == 0) {
            throw new BusinessException(BizErrorCode.AUTH_LOGIN_NO_PERMISSION);
        }
        //        开启房间审核并且房间审核状态不为审核通过开启直播时抛出异常
        checkRoomLiveStartAudit(roomsEntityDTO);


        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .status(com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START)
                .roomId(roomId).build());

        redisTemplate.opsForValue().set(RedisKey.ROOM_LIVE_START_CHANGE_STATUS_KEY + roomsEntityDTO.getRoomId(), RoomStatusEnum.TO_BE_BROADCAST.getStatus(), 2, TimeUnit.DAYS);

        // 开播之后 把对应的账号id 加到 app并发控制级别里面
        redisTemplate.opsForSet().add(RedisKey.LIVING_ROOMS_OF_APPS, roomsEntityDTO.getAccountId());


        //开始直播之后把 当前直播间 id 加入到 账号下的直播房间set   主要 用于做并发控制
        redisTemplate.opsForSet().add(RedisKey.LIVING_ROOMS_OF_ACCOUNT + roomsEntityDTO.getAccountId(), roomsEntityDTO.getIlId());

        Boolean clear = clearGlobal(roomsEntityDTO, com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START, roomsEntityDTO.getStatus());
        if (Boolean.FALSE.equals(clear)) {
            throw new BusinessException(BizErrorCode.BIZ_START_LIVE_FAILED);
        }
        if (StringUtils.isBlank(startType)) {
            startType = "1";
        }
        createRoomExtend(roomId, startType);

        setChannelSwitchOptions(roomsEntityDTO.getChannelId(), thirdPartyUserId);

        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.START_TYPE, startType);
        redisTemplate.delete(RedisKey.ROOM_GLOBAL + roomId);
        //当前登录用户id  由于现在还没有做登录  先 写固定值

        String body = createStartLive(String.valueOf(thirdPartyUserId));
        //房间消息
        paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
        //通知消息
        paasService.sendMessage(roomsEntityDTO.getNifyChannel(), body);
        return true;
    }

    public void setChannelSwitchOptions(String channelId, Integer accountId) {
        Map<String, Object> map = new HashMap<>(8);
//        app_id: fGWLbDnj
//        client: pc_browser
//        vss_token: access:fGWLbDnj:7263fa4c753af99d
//        channel_id: ch_6Euv24U3
//        third_party_user_id: 15197
//        switch: 2
//        switch_options: 2
//        from: js
//        token: 7b5f9a882f427471
        map.put("switch", 2);
        map.put(PaasCommonConstant.CHANNEL_ID_KEY, channelId);
        map.put(PaasCommonConstant.THIRD_PARTY_USER_ID_KEY, accountId);
        map.put(PaasCommonConstant.CLIENT_KEY, "pc_browser");
        map.put("switch_options", 2);
        paasService.setChannelSwitchOptions(map);
    }

    private void openChannelSwitch(String channel, Integer uid) {
        Map<String, Object> map = new HashMap<>();
        map.put("switch", 2);
        map.put(PaasCommonConstant.CHANNEL_ID_KEY, channel);
//        map.put(PaasCommonConstant.THIRD_PARTY_USER_ID_KEY, uid);
        paasService.setChannelSwitch(map);
    }

    @Override
    public void checkRoomLiveStartAudit(RoomsEntityDTO roomsEntityDTO) {
        if (RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch)
                && !RoomAuditStatusEnum.APPROVED.getAuditStatus().equals(roomsEntityDTO.getAuditStatus())) {
            throw new BusinessException(BizErrorCode.COMP_ROOM_AUDIT_STATUS_ERROR);
        }
    }


    @Override
    public JSONObject listCustomMenu(CustomMenuListVO reqVO) {
        QueryWrapper<RoomSupplyEntity> where = new QueryWrapper<>();
        where.eq(ROOM_ID, reqVO.getRoomId());
        where.isNull("deleted_at");
        where.last(RoomConstant.LIMIT_1);
        RoomSupplyEntity supply = supplyMapper.selectOne(where);
        if (Objects.isNull(supply)) {
            return null;
        }
        JSONObject obj = new JSONObject();
        obj.put("id", supply.getIlId());
        obj.put(ROOM_ID, supply.getRoomId());
        obj.put("custom_tag", supply.getCustomTag());
        return obj;
    }

    @Override
    public Integer checkIsAnchor(Integer currAccountId, String roomId) {
        if (Objects.isNull(currAccountId)) {
            throw new BusinessException(BizErrorCode.EMPTY_USER);
        }
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
        if (Objects.isNull(roomInfo)) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (roomInfo.getAccountId().equals(currAccountId)) {
            return 1;
        }
        return 0;
    }

    /**
     * 构造 和 pass发 推流的消息体
     *
     * @param accountId
     * @return
     */
    private String createStartLive(String accountId) {
        JSONObject json = new JSONObject();
        json.put("type", com.vhall.component.plugin.common.constant.RoomConstant.LIVE_START);
        json.put(RoomConstant.ROOM_JOIN_ID, accountId);
        return json.toJSONString();
    }

    /**
     * 创建 roomExtend  并且删除缓存数据
     *
     * @param roomId
     * @param startType
     */
    private void createRoomExtend(String roomId, String startType) {
        RoomExtendsEntity entity = new RoomExtendsEntity();
        entity.setRoomId(roomId);
        LocalDateTime now = LocalDateTime.now();
        entity.setStartTime(now);
        entity.setCreatedAt(now);
        entity.setUpdatedAt(now);
        entity.setStartType(Integer.parseInt(startType));
        roomExtendsMapper.insert(entity);
    }

    /**
     * 清除全局设置
     *
     * @param roomsEntityDTO
     * @param onLive
     * @param status
     */
    private Boolean clearGlobal(RoomsEntityDTO roomsEntityDTO, int onLive, Integer status) {
        if (onLive != status) {
            RoomJoinsEntity join = getHostRoomJoin(roomsEntityDTO.getRoomId());
            clearGlobalRedis(roomsEntityDTO.getRoomId(), join);
            if (onLive == com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START) {
                return initGlobalRedis(roomsEntityDTO, join);
            }
        }
        return true;
    }

    /**
     * 初始化 全局redis配置
     *
     * @param roomsEntityDTO
     */
    private Boolean initGlobalRedis(RoomsEntityDTO roomsEntityDTO, RoomJoinsEntity join) {
        Boolean resp = addSpeaker(roomsEntityDTO, join);
        setMainScreen(roomsEntityDTO, join);
        setDocPermission(roomsEntityDTO, join);
        return resp;
    }

    /**
     * 设置文档
     *
     * @param join
     */
    private void setDocPermission(RoomsEntityDTO roomsEntityDTO, RoomJoinsEntity join) {
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + join.getRoomId(), com.vhall.component.plugin.common.constant.RoomConstant.DOC_PERMISSION, join.getAccountId().toString());
        String body = createMainScreenOrDocBody(join, com.vhall.component.plugin.common.constant.RoomConstant.VRTC_SPEAKER_SWITCH);
        paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
    }

    /**
     * 设置主屏幕
     *
     * @param
     */
    private void setMainScreen(RoomsEntityDTO roomsEntityDTO, RoomJoinsEntity join) {
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + join.getRoomId(), com.vhall.component.plugin.common.constant.RoomConstant.MAIN_SCREEN, join.getAccountId().toString());
        String body = createMainScreenOrDocBody(join, com.vhall.component.plugin.common.constant.RoomConstant.VRTC_BIG_SCREEN);
        paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
    }

    /**
     * 构造 mainScreen消息体
     *
     * @param join
     * @return
     */
    private String createMainScreenOrDocBody(RoomJoinsEntity join, String type) {
        JSONObject json = new JSONObject();
        json.put("type", type);
        json.put(RoomConstant.ROOM_JOIN_ID, join.getAccountId());
        json.put(RoomConstant.NICK_NAME, join.getNickname());
        json.put("room_role", join.getRoleName());
        json.put("prohibit_speak", join.getIsBanned());
        json.put("kicked_out", join.getIsKicked());
        json.put("device_type", join.getDeviceType());
        json.put("device_status", join.getDeviceStatus());
        json.put("avatar", join.getAvatar());
        json.put("target_id", join.getAccountId());
        return json.toJSONString();
    }

    /**
     * 添加上麦用户
     *
     * @param join
     */
    private Boolean addSpeaker(RoomsEntityDTO roomsEntityDTO, RoomJoinsEntity join) {
        if (!com.vhall.component.plugin.common.constant.RoomConstant.HOST.equals(join.getRoleName())) {
            Object val = redisTemplate.opsForValue().get(RedisKey.INTERACT_HANDSUP + join.getAccountId().toString());
            if (val != null) {
                //后面还有逻辑 等会写
            }
        }
        boolean hasExist = redisTemplate.opsForHash().hasKey(RedisKey.INTERACT_SPEAKER + join.getRoomId(), join.getAccountId().toString());
        if (!hasExist) {
            int speakerCount = getSpeakerCount(join);
            if (speakerCount < com.vhall.component.plugin.common.constant.RoomConstant.MAX_SPEAKER) {
                String json = createSpeakerUsers(join);
                redisTemplate.opsForHash().put(RedisKey.INTERACT_SPEAKER + join.getRoomId(), join.getAccountId().toString(), json);
                redisTemplate.opsForSet().remove(RedisKey.INTERACT_INVITE + join.getRoomId(), join.getAccountId());
                redisTemplate.delete(RedisKey.INTERACT_HANDSUP + join.getAccountId());
                String body = createSpeakeUp(join);
                paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
            } else {
                throw new BusinessException(BizErrorCode.BIZ_SPEAKER_FULL);
            }
        } else {
            throw new BusinessException(BizErrorCode.BIZ_REPEAT_SPEAKER);
        }
        return true;
    }

    /**
     * 构造上麦消息
     *
     * @param join 参数
     * @return 返回值
     */
    private String createSpeakeUp(RoomJoinsEntity join) {
        JSONObject json = new JSONObject();
        json.put("type", com.vhall.component.plugin.common.constant.RoomConstant.VRTC_CONNECT_SUCCESS);
        json.put(RoomConstant.ROOM_JOIN_ID, join.getAccountId());
        //昵称
        json.put(RoomConstant.NICK_NAME, join.getNickname());
        //用户角色（1:老师 2:学员 3:助教 4:嘉宾 5:监课）
        json.put("room_role", join.getRoleName());
        //是否禁言Y是N否
        json.put("prohibit_speak", join.getIsBanned());
        //是否踢出Y是N否
        json.put("kicked_out", join.getIsKicked());
        //设备类型0其他1手机端2PC
        json.put("device_type", join.getDeviceType());
        //设备状态0未检测1可上麦2不可上麦
        json.put("device_status", join.getDeviceStatus());
        //头像
        json.put("avatar", join.getAvatar());
        //麦克风开关on开off关
        json.put("vrtc_audio_status", com.vhall.component.plugin.common.constant.RoomConstant.ON);
        //摄像头开关on开off关
        json.put("vrtc_video_status", com.vhall.component.plugin.common.constant.RoomConstant.ON);
        //连麦类型apply主动申请上麦invite主持人邀请上麦
        json.put("vrtc_connect_type", com.vhall.component.plugin.common.constant.RoomConstant.HOST);
        return json.toJSONString();
    }

    /**
     * 构造上麦用户json
     *
     * @param join 参数
     * @return 返回值
     */
    private String createSpeakerUsers(RoomJoinsEntity join) {
        JSONObject json = new JSONObject();
        json.put(RoomConstant.NICK_NAME, join.getNickname());
        json.put("role_name", join.getRoleName());
        json.put("account_id", join.getAccountId());
        json.put("audio", com.vhall.component.plugin.common.constant.RoomConstant.HOST);
        json.put("video", com.vhall.component.plugin.common.constant.RoomConstant.HOST);
        return json.toJSONString();
    }

    /**
     * 获取上麦总数
     *
     * @param join 参数
     * @return 返回值
     */
    private int getSpeakerCount(RoomJoinsEntity join) {
        String roomId = join.getRoomId();
        Long speakerCount;
        speakerCount = redisTemplate.opsForHash().size(RedisKey.INTERACT_SPEAKER + roomId);
        Object docPermission = redisTemplate.opsForHash()
                .get(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.DOC_PERMISSION);
        //文档操作人
        //文档操作人没有上麦也占位置
        if (docPermission != null) {
            String doc = docPermission.toString();
            boolean hasExist = redisTemplate.opsForHash().hasKey(RedisKey.INTERACT_SPEAKER + roomId, doc);
            if (!hasExist && !doc.equals(String.valueOf(join.getAccountId()))) {
                speakerCount++;
            }
        }
        return speakerCount.intValue();
    }

    /**
     * 清除全局得redis设置
     * * @param roomId
     */
    private void clearGlobalRedis(String roomId, RoomJoinsEntity join) {
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.IS_HANDSUP);
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.IS_DOC);
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.DEFINITION);
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.START_TYPE);
        redisTemplate.delete(RedisKey.INTERACT_SPEAKER + roomId);
        redisTemplate.delete(RedisKey.INTERACT_INVITE + roomId);
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.ALL_BANNED);


        //开播或者下播时主讲人和文档操作权限改为主持人自己
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.DOC_PERMISSION, join.getAccountId().toString());
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, com.vhall.component.plugin.common.constant.RoomConstant.MAIN_SCREEN, join.getAccountId().toString());
    }

    @Override
    public Boolean endLive(EndLiveVO reqVO) {
        VssToken account = VssTokenThreadLocal.getToken();
        String roomId = reqVO.getRoomId();

        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
        if (!StringUtils.isBlank(reqVO.getVodId())) {
            // 先暂时不写 后面加上
        } else {
            // 校验是否是主持人
            Integer thirdPartyUserId = account.getThirdPartyUserId();
            if (checkIsAnchor(thirdPartyUserId, roomId) == 0) {
                throw new BusinessException(BizErrorCode.AUTH_LOGIN_NO_PERMISSION);
            }
            if (endLiveMain(roomId, roomsEntityDTO, thirdPartyUserId)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 房间结束的主方法
     *
     * @param roomId
     * @param roomsEntityDTO
     * @param thirdPartyUserId
     * @return
     */
    @Override
    public Boolean endLiveMain(String roomId, RoomsEntityDTO roomsEntityDTO, Integer thirdPartyUserId) {
        RoomJoinsEntity join = getHostRoomJoin(roomId);
        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .status(com.vhall.component.plugin.common.constant.RoomConstant.STATUS_STOP)
                .roomId(roomId).build());

        //结束第三方推流
        stopPushStream(roomsEntityDTO);

        //清理 转播代码
        stopBroadCast(roomsEntityDTO);
        //清理 转播代码

        //直播结束之后 马上 将当前 直播间的连接数清理为0
        redisTemplate.opsForValue().set(RedisKey.CONNECT_COUNT_BY_ROOM + roomsEntityDTO.getIlId(), 0);

        //直播结束之后  将账号下面对应的 房间id  清除 set
        redisTemplate.opsForSet().remove(RedisKey.LIVING_ROOMS_OF_ACCOUNT + roomsEntityDTO.getAccountId(), roomsEntityDTO.getIlId());

        //将房间内的文档进行关闭
        redisTemplate.opsForZSet().removeRangeByScore(RedisKey.ROOM_DOC, roomsEntityDTO.getIlId(), roomsEntityDTO.getIlId());

        //发送消息  关闭抽奖
        try {
            StringRecord rmsg = StreamRecords.string(Collections.singletonMap("data", roomId))
                    .withStreamKey(streamName);
            RecordId recordId = stringRedisTemplate.opsForStream().add(rmsg);
            log.info("抽奖消息发送成功,返回值为：[{}],具体消息为：[{}]", recordId, rmsg);
        } catch (Exception e) {
            log.error("抽奖消息发送失败", e);
            return true;
        }
        clearGlobalRedis(roomsEntityDTO.getRoomId(), join);
        RoomExtendsEntity roomExtends = getRoomExtends(roomId);
        if (roomExtends != null) {
            roomExtends.setEndTime(LocalDateTime.now());
            roomExtends.setEndType(Integer.parseInt(com.vhall.component.plugin.common.constant.RoomConstant.HOST));
            roomExtendsMapper.updateById(roomExtends);
            //放入缓存
            roomCacheService.cacheRoomExtend(roomExtends);
            // 发送异步消息队列，处理上报数据
            roomMqManager.sendBigDataBroadcastRecordReportMessage(roomExtends.getId());
        }
        //====删除rtmp========
        String body = createEndLiveBody(thirdPartyUserId);
        //房间消息
        paasService.sendMessage(roomsEntityDTO.getChannelId(), body);
        //通知消息
        paasService.sendMessage(roomsEntityDTO.getNifyChannel(), body);
        //拉流的状态下去结束推流地址
        endPullStream(roomsEntityDTO);
        return false;
    }

    /**
     * 结束拉流
     *
     * @param roomsEntityDTO
     */
    private void endPullStream(RoomsEntityDTO roomsEntityDTO) {
        Integer pullStreamConfigId = roomsEntityDTO.getPullStreamConfigId();
        Integer pullStreamStatus = roomsEntityDTO.getPullStreamStatus();
        if (pullStreamConfigId != 0 && Objects.equals(pullStreamStatus, RoomPullStreamEnum.PULL_STREAM_ENUM.start())) {
            paasService.submitDelPullStream(pullStreamConfigId);
            roomInnerService.updateByIl(RoomsEntityDTO.builder()
                    .ilId(roomsEntityDTO.getIlId())
                    .pullStreamStatus(RoomPullStreamEnum.PULL_STREAM_ENUM.end())
                    .updatedAt(LocalDateTime.now()).build());
        }
    }

    private void stopPushStream(RoomsEntityDTO roomsEntityDTO) {
        LambdaQueryWrapper<PushStreamEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PushStreamEntity::getRoomId, roomsEntityDTO.getRoomId());
        wrapper.eq(PushStreamEntity::getStatus, 1);
        List<PushStreamEntity> pushStreamEntityList = pushStreamMapper.selectList(wrapper);
        if (Objects.nonNull(pushStreamEntityList) && !pushStreamEntityList.isEmpty()) {
            for (PushStreamEntity entity : pushStreamEntityList) {
                try {
                    Map<String, Object> param = Maps.newHashMap();
                    Integer configId = entity.getConfigId();
                    param.put("config_id", configId);
                    paasService.deleteStreamConfig(param);
                } catch (Exception e) {
                    log.warn("PaaS停止推流异常:{}", e.getMessage());
                }

                entity.setStatus(2);
                entity.setEndTime(LocalDateTime.now());
                pushStreamMapper.updateById(entity);
            }
        }

    }

    private void stopBroadCast(RoomsEntityDTO roomsEntityDTO) {


        LambdaQueryWrapper<RebroadCastEntity> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(RebroadCastEntity::getRoomId, roomsEntityDTO.getRoomId());


        List<RebroadCastEntity> casts = rebroadCastMapper.selectList(wrapper);

        if (Objects.nonNull(casts) && !casts.isEmpty()) {
            for (RebroadCastEntity cast : casts) {
                cast.setEndTime(LocalDateTime.now());
                cast.setStatus(0);
                rebroadCastMapper.updateById(cast);

                redisTemplate.delete(RedisKey.REBROADCAST_INFO + roomsEntityDTO.getRoomId());

                paasService.pullStream(cast.getRoomId(), cast.getSourceRoomId(), cast.getId(), "stop");

                RoomsEntityDTO target = roomInnerService.getRoomInfo(cast.getSourceRoomId());
                setChannel(roomsEntityDTO, target);
                HashMap<String, Object> data = new HashMap<>(4);
                data.put("type", "live_broadcast_stop");
                data.put("channel_id", target.getChannelId());
                paasService.sendMessage(roomsEntityDTO.getChannelId(), JSON.toJSONString(data));
            }
        }


    }

    private void setChannel(RoomsEntityDTO roomsEntityDTO, RoomsEntityDTO targetRoom) {


        HashMap<String, Object> map = new HashMap<>(4);
        map.put(roomsEntityDTO.getChannelId(), roomsEntityDTO.getRoomId());
        log.info("结束直播调用setTargetChannel的参数是: " + targetRoom.getChannelId(), "map是: " + JsonUtil.toJsonString(map));
        paasService.setTargetChannel(targetRoom.getChannelId(), JsonUtil.toJsonString(map));


    }

    private String createEndLiveBody(VssToken account) {
        JSONObject json = new JSONObject();
        json.put("type", com.vhall.component.plugin.common.constant.RoomConstant.LIVE_OVER);
        json.put(RoomConstant.ROOM_JOIN_ID, account.getThirdPartyUserId());
        return json.toJSONString();
    }

    private String createEndLiveBody(Integer accountId) {
        JSONObject json = new JSONObject();
        json.put("type", com.vhall.component.plugin.common.constant.RoomConstant.LIVE_OVER);
        json.put(RoomConstant.ROOM_JOIN_ID, accountId);
        return json.toJSONString();
    }

    @Override
    public RoomDetailRespVO getRoomDetail(RoomAttrVO reqVO) {
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        RoomDetailRespVO vo = new RoomDetailRespVO();
        vo.setAppId(roomsEntityDTO.getAppId());
        vo.setAccountId(roomsEntityDTO.getAccountId());
        vo.setChannelId(roomsEntityDTO.getChannelId());
        vo.setSubject(roomsEntityDTO.getSubject());
        vo.setCreatedAt(roomsEntityDTO.getCreatedAt());
        return vo;
    }


    @Override
    public List<String> delete(DelRoomVO reqVO) {
        List<String> result = new ArrayList<>();
        List<String> roomIds = Splitter.on(",").splitToList(reqVO.getRoomId());
        LambdaQueryWrapper<RoomsEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomsEntity::getStatus, com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START);
        wrapper.in(RoomsEntity::getRoomId, roomIds);
        Integer count = roomMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BusinessException(BizErrorCode.BIZ_LIVING_NOT_DELETE);
        }
        for (String single : roomIds) {
            LambdaQueryWrapper<RoomsEntity> select = Wrappers.lambdaQuery();
            select.eq(RoomsEntity::getRoomId, single)
                    .last("limit 1");
            RoomsEntity roomsEntity = roomMapper.selectOne(select);
            if (roomsEntity != null && roomsEntity.getStatus() != com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START) {
                if (roomsEntity.getStatus().equals(com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START)) {
                    throw new BusinessException(BizErrorCode.BIZ_LIVING_NOT_DELETE);
                }
                roomMapper.deleteById(roomsEntity.getIlId());
                delPassRoom(roomsEntity);
                redisTemplate.delete(RedisKey.WARM_INFO + roomsEntity.getIlId());
                //删除房间缓存信息
                redisTemplate.delete(RedisKey.ROOM_INFO_BY_ROOM_ID + roomsEntity.getRoomId());
                result.add(single);
            }
        }
        return result;
    }

    /**
     * 删除pass房间
     *
     * @param room
     */
    private void delPassRoom(RoomsEntity room) {
        paasService.delRoom(room.getRoomId());
        paasService.delInav(room.getInavId());
        paasService.delChannel(room.getChannelId());
    }

}
